package main

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/yosssi/gmq/mqtt"
	"github.com/yosssi/gmq/mqtt/client"
	"io/ioutil"
	"net/http"
	"recharge/guanbo"
	"recharge/logger"
	"strings"
	"time"
)

//插座充电电流为0检测统计
var chargingNumber = map[string]int{}

//博观察
//订阅设备
func BGSubscribeDeviceByID(did string) error {
	logger.Log.Info("[订阅设备]", did)
	err := cli.Subscribe(&client.SubscribeOptions{
		SubReqs: []*client.SubReq{
			&client.SubReq{
				TopicFilter: []byte(fmt.Sprintf("/cp/d/%s/SocketList", did)),
				QoS:         mqtt.QoS0,
				// 接收设备消息
				Handler: func(topicName, message []byte) {
					logger.Log.Debug("[设备插座列表回传信息]", string(topicName), string(message))
					var c guanbo.SocketList
					err := proto.Unmarshal(message, &c)
					if err != nil {
						logger.Log.Error(err)
						return
					}
					logger.Log.Info(c.List)
					sockets := ""
					ids := make([]int32, 0)
					db := GetDB()

					//更新插座列表
					for _, v := range c.List {
						sockets += fmt.Sprintf("%d,", v.Soketid)
						db.Table(new(DeviceNumber).TableName()).Where(&DeviceNumber{EID: did, Number: fmt.Sprintf("%d", v.Soketid)}).
							FirstOrCreate(&DeviceNumber{EID: did, UpdatedAt: time.Now(),
								Number: fmt.Sprintf("%s", v.Soketid), State: 0,
								DisplayNumber: fmt.Sprintf("%d", v.Soketid)})
						//表明在线
						db.Table(new(DeviceNumber).TableName()).Where(&DeviceNumber{EID: did, Number: fmt.Sprintf("%d", v.Soketid)}).
							Update(&DeviceNumber{UpdatedAt: time.Now()})
						if v.Status != guanbo.SocketStatus_online { //故障
							db.Table(new(DeviceNumber).TableName()).Where(&DeviceNumber{EID: did, Number: fmt.Sprintf("%d", v.Soketid)}).
								Update(&DeviceNumber{State: 2})
						}
						if v.Status == guanbo.SocketStatus_online {
							// logger.Log.Debug("变更插座状态", did, v.Soketid)
							db.Exec(fmt.Sprintf("update `equipment_nums` set state=0 where "+
								"eid=%s and number=%d and state!=1 ", did, v.Soketid))
						}
						ids = append(ids, v.Soketid)
					}
					logger.Log.Info("[更新设备状态]", did, "是否自动上报", !c.ScanReturn)
					err = db.Table(new(Device).TableName()).Where(&Device{EquipmentNum: did}).Update(&Device{SocketNums: sockets,
						UpdatedAt: time.Now()}).Error
					// db.Table(new(DeviceNumber).TableName()).Where("eid=? and number not in (?)", did, ids).Update(
					// 	&DeviceNumber{
					// 		State: 2, //已经不在改设备下 则插座损坏
					// 	})
				},
			},
		},
	})
	err = cli.Subscribe(&client.SubscribeOptions{
		SubReqs: []*client.SubReq{
			&client.SubReq{
				TopicFilter: []byte(fmt.Sprintf("/cp/d/%s/setSocket", did)),
				QoS:         mqtt.QoS0,
				// 接收设备消息
				Handler: func(topicName, message []byte) {
					logger.Log.Debug("[设备插座列表回传信息]", string(topicName), string(message))
					var c guanbo.SetSocket
					err := proto.Unmarshal(message, &c)
					if err != nil {
						logger.Log.Error(err)
						return
					}
					logger.Log.Info(c.OldSocketId, c.NewSocketId, c.Result)
				},
			},
		},
	})
	err = cli.Subscribe(&client.SubscribeOptions{
		SubReqs: []*client.SubReq{
			&client.SubReq{
				TopicFilter: []byte(fmt.Sprintf("/cp/d/%s/ChargeProcess", did)),
				QoS:         mqtt.QoS0,
				// 接收设备消息
				Handler: func(topicName, message []byte) {
					logger.Log.Debug("[设备充电状态回传信息]", string(topicName), string(message))
					var c guanbo.ChargeProcess
					err := proto.Unmarshal(message, &c)
					if err != nil {
						logger.Log.Error(err)
						return
					}
					db := GetDB()
					deviceID := strings.ReplaceAll(string(topicName), "/cp/d/", "")
					deviceID = strings.ReplaceAll(deviceID, "/ChargeProcess", "")
					switch c.Status {
					case guanbo.ChargeStatus_charging: //充电中检测
						//充电中 如果电流为0 充电时间超过100秒
						//查询订单 是否需要关闭
						dn := &DeviceNumber{}
						db.Debug().Table(new(DeviceNumber).TableName()).
							Where(&DeviceNumber{EID: deviceID, Number: fmt.Sprintf("%d", c.SocketID)}).First(dn)
						needClose := true
						if dn.ID > 0 {
							om := &Order{}
							db.Debug().Table(new(Order).TableName()).Where("state=0").
								Where(&Order{EquidmentId: fmt.Sprintf("%d", dn.ID)}).First(om)
							if om.ID > 0 {
								logger.Log.Info("[recharging]", om.ID)
								//设备自动关闭 取消
								needClose = false
							}
						}
						if needClose && c.Chargingtime >= 60 { //如果没有订单 就必须断电处理
							_ = GBEndRecharge(deviceID, int(c.SocketID), 0, 0, 0)
						} else {
							_ = GBReplyChargingStatus(deviceID, c.SocketID)
						}
					case guanbo.ChargeStatus_endoftimeout: //充电时间到 设备端行为
						logger.Log.Info("[ChargeStatus_endoftimeout]")
						fallthrough
					case guanbo.ChargeStatus_connecttimeout: //连接超时 设备端行为
						logger.Log.Info("[ChargeStatus_connecttimeout]")
						fallthrough
					case guanbo.ChargeStatus_chargetimeout: //充电超时 设备端行为
						logger.Log.Info("[ChargeStatus_chargetimeout]")
						fallthrough
					case guanbo.ChargeStatus_breaksocket: //插座断开 设备端行为
						logger.Log.Info("[ChargeStatus_breaksocket]")
						_ = GBEndRecharge(deviceID, int(c.SocketID), 0, 0, 0)
						CallbackOrder(deviceID, c.SocketID)
					}
					db.Table(new(Device).TableName()).Where(&Device{EquipmentNum: deviceID}).Update(&Device{UpdatedAt: time.Now()})
					db.Table(new(DeviceNumber).TableName()).Where(&DeviceNumber{EID: deviceID, Number: fmt.Sprintf("%d", c.SocketID)}).
						Update(&DeviceNumber{UpdatedAt: time.Now()})
					logger.Log.Info("充电时间", c.Chargingtime, "秒",
						"累记电流", c.Chargingenergy, "度",
						"充电电流", c.Socketvoltage, "安",
						"插座功率", c.Socketpower, "瓦",
						"充电插座号", c.SocketID, "充电状态", c.GetStatus())
				},
			},
		},
	})
	err = cli.Subscribe(&client.SubscribeOptions{
		SubReqs: []*client.SubReq{
			&client.SubReq{
				TopicFilter: []byte(fmt.Sprintf("/cp/d/%s/ClientInfo", did)),
				QoS:         mqtt.QoS0,
				// 接收设备消息
				Handler: func(topicName, message []byte) {
					logger.Log.Debug("[设备登录成功]", did, string(topicName), string(message))
					var c guanbo.ClientInfo
					err := proto.Unmarshal(message, &c)
					if err != nil {
						logger.Log.Error(err)
						return
					}
					db := GetDB()
					db.Table(new(Device).TableName()).Where(&Device{EquipmentNum: did}).Update(&Device{UpdatedAt: time.Now()})
					time.Sleep(3 * time.Second)
					err = GBRspClientInfo(did, defaultRatedPower, defaultMaxRechargeTime, defaultPeriodTime,
						defaultContinueTime, defaultContinuePower, defaultEndTime, defaultMinPower)
					if err != nil {
						logger.Log.Error(err)
					}
					time.Sleep(5 * time.Second)
					//获取插座
					err = GBGetDeviceSocketList(did, 0, 0, ACTION_OPEN, MSG_TYPE_CONTROL)
					if err != nil {
						logger.Log.Error(err)
					}
				},
			},
		},
	})
	return err
}

func CallbackOrder(deviceID string, socketID int32) {
	logger.Log.Info("[close order]")
	var r http.Request
	_ = r.ParseForm()
	r.Form.Add("deviceID", deviceID)
	r.Form.Add("channelID", fmt.Sprintf("%d", socketID))
	bodystr := strings.TrimSpace(r.Form.Encode())
	request, err := http.NewRequest("POST", DEVICE_CALL_BACK_URL, strings.NewReader(bodystr))
	if err != nil {
		logger.Log.Error(err)
		return
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	var resp *http.Response
	resp, err = http.DefaultClient.Do(request)
	if err != nil {
		logger.Log.Error(err)
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Log.Error(err)
		return
	}
	logger.Log.Info(string(b))
}

//开始充电
func GBStartRecharge(did string, channelID, maxTime, action, msgType int) error {
	//先 结束充电
	_ = GBEndRecharge(did, channelID, 0, 0, 0)
	var err error
	go func() {
		time.Sleep(2 * time.Second)
		c := &guanbo.ServerStarCharge{} //设备指令
		c.Chargefull = true
		c.SocketID = int32(channelID)
		c.MaxChargeTime = int32(maxTime)
		c.Ts = time.Now().Unix()
		logger.Log.Info("[给指定设备通电]", fmt.Sprintf("/cp/s/%s/ServerStarCharge", did), c.String())
		b, _ := proto.Marshal(c)
		// Publish a message.
		err = cli.Publish(&client.PublishOptions{
			QoS:       mqtt.QoS0,
			TopicName: []byte(fmt.Sprintf("/cp/s/%s/ServerStarCharge", did)),
			Message:   b,
		})
	}()

	return nil
}

//设置插座编号
func GBSetDeviceNumber(did string, number, newNumber int64) error {
	c := &guanbo.SetSocket{} //设备指令
	c.NewSocketId = newNumber
	c.OldSocketId = number
	c.Ts = time.Now().Unix()
	logger.Log.Info("[设置指定插座]", fmt.Sprintf("/cp/s/%s/setSocket", did), c.String())
	b, err := proto.Marshal(c)
	// Publish a message.
	err = cli.Publish(&client.PublishOptions{
		QoS:       mqtt.QoS0,
		TopicName: []byte(fmt.Sprintf("/cp/s/%s/setSocket", did)),
		Message:   b,
	})
	return err
}

//返回计费状态 每3分钟记一次费用
func GBReplyChargingStatus(did string, channelID int32) error {
	c := &guanbo.ChargeProcessControl{} //设备指令
	c.SocketID = channelID
	c.Status = guanbo.ChargeStatus_success
	logger.Log.Info("[服务器计费成功]", fmt.Sprintf("/cp/s/%s/ChargeProcessControl", did), c.String())
	b, _ := proto.Marshal(c)
	// Publish a message.
	err := cli.Publish(&client.PublishOptions{
		QoS:       mqtt.QoS0,
		TopicName: []byte(fmt.Sprintf("/cp/s/%s/ChargeProcessControl", did)),
		Message:   b,
	})
	return err
}

//结束充电
func GBEndRecharge(did string, channelID, maxTime, action, msgType int) error {
	c := &guanbo.ChargeProcessControl{} //设备指令
	c.SocketID = int32(channelID)
	c.Status = guanbo.ChargeStatus_endofuser
	logger.Log.Info("[结束给设备充电]", fmt.Sprintf("/cp/s/%s/ChargeProcessControl", did), c.String())
	b, err := proto.Marshal(c)
	// Publish a message.
	err = cli.Publish(&client.PublishOptions{
		QoS:       mqtt.QoS0,
		TopicName: []byte(fmt.Sprintf("/cp/s/%s/ChargeProcessControl", did)),
		Message:   b,
	})
	return err
}

//回复设备信息
func GBRspClientInfo(did string, ratedpower, maxChargeTime, periodTime, continueTime, continuePower, endTime, minPower int32) error {
	c := &guanbo.ClientInforesp{} //设备指令
	c.Ratedpower = ratedpower
	c.MaxChargeTime = maxChargeTime
	c.Periodtime = periodTime
	c.ContinueTime = continueTime
	c.ContinuePower = continuePower
	c.EndTime = endTime
	c.MinPower = minPower
	c.Ts = time.Now().Unix()
	logger.Log.Info("[设置设备基本参数]", fmt.Sprintf("/cp/s/%s/ClientInforesp", did), c.String())
	b, err := proto.Marshal(c)
	// Publish a message.
	err = cli.Publish(&client.PublishOptions{
		QoS:       mqtt.QoS0,
		TopicName: []byte(fmt.Sprintf("/cp/s/%s/ClientInforesp", did)),
		Message:   b,
	})
	return err
}

//获取插座列表
func GBGetDeviceSocketList(did string, channelID, maxTime, action, msgType int) error {
	c := &guanbo.ScanSockeID{} //设备指令
	c.Timeout = 5
	c.Ts = time.Now().Unix()
	logger.Log.Info("[获取插座列表]", fmt.Sprintf("/cp/s/%s/ScanSockeID", did), c.String())
	b, err := proto.Marshal(c)
	// Publish a message.
	err = cli.Publish(&client.PublishOptions{
		QoS:       mqtt.QoS0,
		TopicName: []byte(fmt.Sprintf("/cp/s/%s/ScanSockeID", did)),
		Message:   b,
	})
	return err
}
